/*  BEAMIN.C  --------------------------------------------------------------

	The receiving side of the BEAMER system. It sits in the back-
	ground waiting for sound and then makes it available via link
	paste to other applications.

	-------------------------------------------------------------------------  */


#include <p_std.h>
#include <p_gen.h>
#include <p_sys.h>
#include <p_file.h>
#include <p_keyb.h>
#include <p_graf.h>
#include <p_screen.h>
#include <p_serial.h>
#include <epoc.h>

#include <lp.h>

#include <wlib.h>
#include <hwif.h>


#define CONTROL_I				9

#define PASTE_BUF_MAX_LEN	0x1000
#define RX_BUF_LEN			1

#define MSG_Q_SIZE			4


typedef struct
	{
	E_MESSAGE				m;
	UWORD						arg1;
	UWORD						arg2;
	} MESS;



GLREF_D TEXT		*	DatCommandPtr;
GLREF_D UWORD			DatLocked;

GLREF_D VOID		*	winHandle;

LOCAL_D UINT			MainWid;
LOCAL_D INT				ret;
LOCAL_D INT				i;
LOCAL_D WMSG_KEY		Key;

LOCAL_D VOID		*	sch;
LOCAL_D VOID		*	timcb;

LOCAL_D HANDLE			WsrvHandle;
LOCAL_D HANDLE			ioSem;
LOCAL_D HANDLE			srvPid;

LOCAL_D WORD			sStat;

LOCAL_D TEXT			PasteBuf[PASTE_BUF_MAX_LEN];
LOCAL_D UINT			PasteBufLen = 0;

LOCAL_D TEXT			RxBuf[RX_BUF_LEN];
LOCAL_D UINT			RxBufLen = 1;



LOCAL_C INT Check(INT val, TEXT *msg)              /*  General purpose check  */
    {
    TEXT		buf[50];

    if (val >= 0)
        return val;
    p_atos(&buf[0],"Error : %s",msg);
    p_notifyerr(val,&buf[0],0,0,0);
    p_exit(0);
    }


	
LOCAL_C VOID GoToBack(VOID)                     /*  Go to end of client list  */
	{
	wClientPosition(WS_LAST_CLIENT_POSITION, 0);
	wFlush();
	}
	
	

LOCAL_C VOID CancelCaptureKey(VOID)
	{
	Check(wCancelCaptureKey(CONTROL_I,
			W_SHIFT_MODIFIER | W_CTRL_MODIFIER,
			W_SHIFT_MODIFIER | W_CTRL_MODIFIER | W_PSION_MODIFIER),
			"cancelling captured key");
	}
	
	

LOCAL_C VOID UnloadPDD(VOID)
	{
	p_devdel("TTY.BEM", E_PDD);
	}


LOCAL_C VOID CleanUpAndQuit()
	{
	UnloadPDD();
	CancelCaptureKey();
	p_exit(0);
	}



LOCAL_C VOID ProcessSystemCommand(VOID)              /*  Listen to the shell  */
	{
	TEXT				buf[P_FNAMESIZE + 2];

	wGetCommand((UBYTE *)&buf[0]);
	if (buf[0] == 'X')
		{
		CleanUpAndQuit();
		p_exit(0);
		}
	}

	
LOCAL_C INT ReadDataWithTimeout()
	{
	INT						SigCount;                 /*  Count of stray signals  */
	INT						PlusCount;                      /*  Terminator count  */

	ULONG						TimLen = 100L;                 /*  10 second timeout  */

	WORD						SerStat;         /*  Status word for the serial read  */
	WORD						TimStat;               /*  Status word for the timer  */

	TEXT					*	pPB;                 /*  Pointer in the paste buffer  */
	TEXT						Plus[2];

	pPB	=	&PasteBuf[0];
	SigCount	=	0;                      /*  Initialise the stray signal count  */
	PlusCount = 0;                         /*  Initialise the plus sign count  */
	Plus[0] = '+';
	Plus[1] = 0;

	Check(p_open(&sch, "TTY.BEM:X", 0xffff), "opening TTY.BEM:X");
   
	p_ioc5(sch, P_FREAD, &SerStat, &RxBuf[0], &RxBufLen);    /*  Queue a read  */
	p_ioc4(timcb, P_FRELATIVE, &TimStat, &TimLen);          /*  Queue a timer  */

	FOREVER
		{
		p_iowait();                                                /*  Wait...  */
		if (SerStat != E_FILE_PENDING)                     /*  Got a character  */
			{
			p_iow(timcb, P_FCANCEL);                       /*  Cancel the timer  */
			p_waitstat(&TimStat);                        /*  Consume the signal  */
			if (p_scmp(&RxBuf[0], &Plus[0]))             /*  If it is not a '+'  */
				{
				pPB = (TEXT *)p_bcpy(pPB, &RxBuf[0], RX_BUF_LEN);
                                         /*  Add received char to the buffer  */
				PasteBufLen++;
				}
			else
				{
				PlusCount++;

				if (PlusCount == 3)
					break;
				}
			p_ioc5(sch, P_FREAD, &SerStat, &RxBuf[0], &RxBufLen);    /*  Requeue */
			p_ioc4(timcb, P_FRELATIVE, &TimStat, &TimLen);    /*  Requeue timer  */
			}
		else if (TimStat != E_FILE_PENDING)
			{
			p_iow(sch, P_FCANCEL);
			p_waitstat(&TimStat);
			Check(p_close(sch), "closing TTY.BEM:X");
			return E_FILE_INACT;
			}						  
		else
			SigCount++;
		}
	
	if (SigCount)                          /*  We have got some spare signals  */
		{
		p_signaln(ioSem, SigCount);                          /*  Put them back  */
		}

	Check(p_close(sch), "closing TTY.BEM:X");
	return 0;
	}



LOCAL_C VOID MainLoop(VOID)
	{
	INT						gen;

	WMSG_KEY					key;
	WORD						KeyStat;
	WORD						mStat;
	MESS					*	pM;
	HANDLE					clientPid;
	INT						iState;

	ULONG						fmt;


	uGetKeyA(&KeyStat, &key);
	p_mreceive(&mStat,&pM);

	clientPid = 0;                                        /*  We are inactive  */
	
	fmt = 0;                                      /*  We are not a server yet  */
	gen = p_msendreceivew(WsrvHandle, SY_LINK_SERVER, &fmt);
	Check(gen, "WSRV 0 server request");

	FOREVER
		{
		p_iowait();
		if (mStat != E_FILE_PENDING)                     /*  We have a message  */
			{
			if (mStat != 0)                            /*  Extra error checking  */
				Check(-1, "PasteServer: 1");
			
			switch (pM->m.type)            /*  Some kind of message has arrived  */
				{
				case TY_LINKSV_STEP:                     /*  Somebody wants data  */
					if (clientPid == 0)      /*  First request, so setup the info  */
						{
						gen = E_GEN_NSUP;
					
						switch (pM->arg1)
							{
							case DF_LINK_TEXT_VAL:
							case DF_LINK_TABTEXT_VAL:
							case DF_LINK_PARAS_VAL:
								clientPid = pM->m.pid;       /*  Register our client  */
								p_logon(clientPid, TY_LINKSV_DEATH);
								iState = 0;                 /*  Set the server state  */
								gen = 0;       /*  We can support the requested type  */
							}
						break;
						}
					
					if (!pM->arg2)    /*  If the client requests no data, give up  */
						iState = 1;              /*  Jump to the end of data state  */
				
					switch (iState)                 /*  Handle the various states  */
						{
						case 0:
							gen = PasteBufLen;           /*  The length of the data  */
							if (gen > pM->arg2) /*  pM->arg2 has the maximum length  */
								gen = pM->arg2;
				
							p_pcpyto(clientPid, (VOID *)pM->arg1, &PasteBuf[0], gen);
							iState++;                 /*  Advance to the next state  */
							break;
						default:
							gen = E_FILE_EOF;            /*  Signifies no more data  */
							p_logoff(clientPid, TY_LINKSV_DEATH);   /*  No interest  */
							clientPid = 0;              /*  We are no longer active  */
						}
					break;
			
				case TY_LINKSV_DEATH:                    /*  Our client has died  */
					clientPid = 0;           /*  Mark ourselves as inactive again  */
					break;        /*  Supervisor does not require a return result  */
				default:               /*  Someone has sent us a naughty message  */
					gen =  E_GEN_NSUP;      /*  Just return a not supported error  */
				}
				
			p_mfree(pM, gen);      /*  Return message to queue. Return a result  */
			p_mreceive(&mStat, &pM);                    /*  Get another message  */
			}
		else if (KeyStat != E_FILE_PENDING)
			{
			if (KeyStat != 0)
				Check(-1, "PasteServer: 2");        /*  Just some extra checking  */

			switch (key.keycode)
				{
				case CONS_EVENT_FOREGROUND:     /*  Refuse to come to foreground  */
					GoToBack();
					break;
				case CONS_EVENT_COMMAND:
					ProcessSystemCommand();                   /*  Listen to shell  */
					break;
				default:
					if ((key.modifiers &
							W_SHIFT_MODIFIER | W_CTRL_MODIFIER) ==
							(W_SHIFT_MODIFIER | W_CTRL_MODIFIER) &&
							key.keycode == (CONTROL_I))
						{
						DatLocked = TRUE;                    /*  Go deaf for a bit  */
						gen = ReadDataWithTimeout();             /*  Get some data  */
						DatLocked = FALSE;                    /*  Not deaf anymore  */
						if (gen < 0)                        /*  Was there an error  */
							{
							if (gen == E_FILE_INACT)                  /*  Timed out  */
								{
								p_notifyerr(gen, "microphone timeout", NULL, NULL, NULL);
								GoToBack();
								}
							else
								{
								Check(gen, "microphone read");
								}
							}
						else
							{
							fmt = DF_LINK_TABTEXT;
							Check(p_msendreceivew(WsrvHandle, SY_LINK_SERVER, &fmt), "giving up server");
							}
						}
				}
			uGetKeyA(&KeyStat, &key);
			}
		else
			{
			Check(-1, "Stray signal death");
			}
		}
	}
	

LOCAL_C VOID CaptureKey(VOID)
	{
	Check(wCaptureKey(CONTROL_I,
			W_SHIFT_MODIFIER | W_CTRL_MODIFIER,
			W_SHIFT_MODIFIER | W_CTRL_MODIFIER | W_PSION_MODIFIER),
			"capturing key");
	}
	
	
LOCAL_C VOID LoadPDD(VOID)
	{
    TEXT				buf[P_FNAMESIZE];

#ifdef DEBUG
	p_scpy(&buf[0], "loc::m:\\app\\beamv1.pdd");
#else
    p_fparse("BEAMV1.PDD", DatCommandPtr, &buf[0], NULL);
#endif
    ret = p_loadpdd(&buf[0]);
    if (ret && ret != E_FILE_EXIST)
		Check(ret, "Loading BEAMV1.PDD");
	}
	
	
LOCAL_C VOID CreateGC(VOID)
	{
	INT					hgc;

	hgc	=	gCreateGC0(MainWid);
	if (hgc	<	0)
		p_exit(hgc);
	}


LOCAL_C VOID SpecificInit(VOID)
	{
	E_PROC					e;


	UnloadPDD();

	MainWid	=	uFindMainWid();
	
	Check(p_minit(MSG_Q_SIZE, sizeof(MESS) - sizeof(E_MESSAGE)), "main: p_minit");

	WsrvHandle	=	p_pidfind("SYS$WSRV.*");
	p_getosd(&e, (VOID *)(p_getpid() & E_PIDMASK), sizeof(E_PROC));
	ioSem = e.semaphore;

	CreateGC();
	LoadPDD();
	CaptureKey();
	hCrackCommandLine();
	p_open(&timcb, "TIM:", 0xffff);
	}
	

GLDEF_C VOID main(VOID)
	{
	uCommonInit();
	SpecificInit();
	MainLoop();
	}

